package org.NooLab.glue.instances;

 
 
import java.util.Arrays; 
import java.util.HashMap;
import java.util.Map;
import java.util.Observer;
import java.util.Vector;

import org.NooLab.utilities.files.DFutils;
import org.NooLab.utilities.logging.PrintLog;
import org.NooLab.utilities.net.GUID;
import org.NooLab.utilities.nums.NumUtilities;
import org.NooLab.utilities.strings.StringsUtil;
import org.NooLab.utilities.timing.*;


import org.NooLab.compare.utilities.math.DistanceControlProperties;
import org.NooLab.glue.MessageBoard;
import org.NooLab.glue.components.DataConnector;
import org.NooLab.glue.components.DataReceiverIntf;
import org.NooLab.glue.components.MessageBoardFactory;
import org.NooLab.glue.components.MessageBoardFactory.RegisterParamPackage;
 
import org.NooLab.glue.components.MessageBoardFactoryProperties;
import org.NooLab.glue.components.ParticipantsUpLinkConnectorIntf;
import org.NooLab.glue.components.Registration;
 
import org.NooLab.glue.components.UpLinkConnectorIntf;
import org.NooLab.glue.components.pkg.TaskPackage;
import org.NooLab.glue.interfaces.ReceptorDigestIntf;
import org.NooLab.glue.msgframe.XMessage;
import org.NooLab.glue.msgframe.XRegistration;
import org.NooLab.glue.net.ComBox;
// import org.NooLab.glue.net.env.TouchBoxIntf;
import org.NooLab.glue.storage.MsgBoardDataStorage;
import org.NooLab.glue.subscription.FilterPackageIntf;
import org.NooLab.glue.subscription.FutureIntf;
import org.NooLab.glue.subscription.FuturesIntf;
import org.NooLab.glue.transaction.Transaction;
import org.NooLab.glue.transaction.Transactions;


/**
 *  
 * TODO: RECEPTORS, which are available, should send AVAIL state message 
 * 		 from time to time to the MessageBoard, in order to not get forgotten  
 *       
 * Central for understanding is the distinction between physical receptors and
 * logical receptors;
 * While the MessageReceptor (RECEPTOR) or DataConnector (SOURCE) implement
 * the link to the physical aspects of wiring (actually, it uses an abstraction of
 * the physical layer through ComBoxFectory), the class "MessageReceptorDigester{}"
 * (see below, it is an inner class) is the logical receptor.
 * It exposes mainly performTask() and performSystemTask() as callbacks from the
 * more physical part of receiving messages   
 *       
 * 
 */
public abstract class MessageBoardParticipant implements ParticipantBaseIntf  
														{

														// , SubscriptionsIntf 	
	// =================================

	// object references ..............   
	 
	protected MessageBoardFactory mbf ;
	private MessageBoardFactory msgboardFactory ;
	MessageBoardFactoryProperties msgBoardFactoryProperties ;
	
	ParticipantFactory factory;
	
	protected XRegistration xRegistration ;

	
	protected TaskPackage taskPackage;
	 
	protected TaskPackage resultPackage;          // for traffic from receptor to MessageBoard
	
	protected InstanceFlavor flavor = new InstanceFlavor();
	
	private ParticipantsDeliveryPadIntf deliveryPad;
	
	
	FuturesIntf futures ;
	
	// inner class for callback .......
	private MessageReceptorDigester mrd  ; // implements ReceptorDigestIntf ;
	
	// private UpLinkConnectorIntf upLinkProcedures ;  // the method performed in the engine
	private  ParticipantsUpLinkConnectorIntf upLinkProcedures;
	private ParticipantUpLinkServiceIntf upLinkService; // the service engine
	
	
	// main variables / properties ....
	String mbConfigPath;
	
	protected String specialContentType = "";
	
	protected String participantsRole="";
	
	String registrationStoragefilename ="" ;
	
	/** by default, for SOURCE=true, for RECEPTOR=false */
	protected boolean instanceIsUnique = false ;
	String uniqueAdHocIdentifier="" ;
	protected boolean hibernationActivated = true;
	private int refuseHibernation=0;
	protected int numberOfConnectRetry = 3;
	protected int delayBetweenConnectRetries = 3000;
	protected double delayProgressionFactor = 3.4;
	
	protected boolean dropDeniedTasks = true;
	protected boolean taskIsCanceled = false;
	
	protected boolean collectResults = false;
	
	protected boolean useUplinkConnectPolling = false ;
	protected int useUplinkConnectPollingPeriodLength = 3000 ;
	
	protected Vector<Long> excludedTransactionIDs = new Vector<Long>();
	
	protected ResultCompletionTriggers resultCompletionTriggers  ; 
	private Vector<String> completedResultBuffers = new Vector<String>() ;
	
	// constants ......................
	
	private String name = "mbinstance" ;
	
	protected Transaction mTransact ;
	
	private int maxDelayBetweenReconnects = 1000*60*60*3 ;
	
	// volatile variables .............
	Vector<Long> acceptedTransactionIDs = new Vector<Long>();
	
	Vector<Long> mrdProcesses = new Vector<Long>();
	
	boolean isCurrentlyInConnectingProcess = false;
	boolean connectingInterrupted = false;
	
	// helper objects .................
	protected PrintLog out = new PrintLog(2,true);
	protected NumUtilities numutil = new NumUtilities();
	protected StringsUtil strgutil = new StringsUtil() ;
	protected DFutils fileutil = new DFutils ();
	
	// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
	public MessageBoardParticipant( String mbconfigpath,String name ){
		
		mbConfigPath = mbconfigpath ;
		
		// we immediately define the adhoc UID,
		// if there is a saved instance in the respective hibernation directory, this will change!
		// if instanceIsUnique==false any available registration will be loaded 
		uniqueAdHocIdentifier = GUID.randomvalue() ;
		
		this.name = name;
		 
	}
	// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
	
	
	
	public void setMessageBoardFactoryProperties( MessageBoardFactoryProperties factoryProperties ){
		
		try{
			this.mbf = new MessageBoardFactory(factoryProperties);

			msgBoardFactoryProperties = factoryProperties;
			msgboardFactory = mbf;
			
			/* if (factoryProperties.getFilter() != null) { // .filter
				msgboardFactory.introduceFilterPackage(factoryProperties.getFilter());
			} */
			
			mbf.restoreOpenTransactions(name, 0);

			// touchbox = mbf.getTouchBox();
			
			mbf.setParticipantsReference( this); // MessageBoardParticipant
			
			resultCompletionTriggers = new ResultCompletionTriggers( mbf, out);
			
			
		} catch(Exception e){
			e.printStackTrace();
		}
	}
 
	/**
	 * this version is used for hibernated connections, re-using the previous instanceID,
	 * the previous applicant's name, etc...
	 * @param xreg
	 * @throws Exception 
	 */
	public int establishConnections( XRegistration xreg, InstanceFlavor flavor , int numberOfConnectRetry ) throws Exception{
		
		int z=0,ticket = -1, result=-1 ;
		String mainRole , applicantName="";
		long instanceID=-1;
		boolean notConnected;
		int currentDelayBetweenConnectRetries;
				
		mainRole = xreg.getRole() ;
		instanceID = xreg.getInstanceID() ;
		applicantName = xreg.getNameOfApplicant() ;
		
		
		try{
			
			currentDelayBetweenConnectRetries = delayBetweenConnectRetries ;
			notConnected = true; 
			connectingInterrupted = false; 
			while ( //(xreg==null) &&
					(notConnected) && (connectingInterrupted==false) && 
					((z<numberOfConnectRetry) || (z<0))){
				
				// this checks for SOURCE and IBOARD, which is running an embedded source client
				if (MessageBoardFactoryProperties.isSourceType(mainRole)){
				// if ( (mainRole.toLowerCase().contentEquals("source")) ){
					try{
						if (xRegistration==null)
						{
							connectSource( ticket, mainRole, instanceID, applicantName, flavor,z );
						}
						
					} catch(Exception e){  
						// educated silence...
						@SuppressWarnings("unused")
 						String errmsg = e.getMessage();
						// e.printStackTrace();
						xRegistration=null;
						return result;
					}
					
				}
				
				if (mainRole.toLowerCase().contentEquals("receptor")) {
					try{
						if (xRegistration==null)
						{
							connectReceptor(ticket, mainRole,xreg, flavor);
						}
					    
					}catch(Exception e){
						out.printlnErr(1, "\n...some critical error occured : "+e.getMessage()+"\r\n") ;
					}
				}
				z++;
				notConnected = (xRegistration == null);
				if ((connectingInterrupted==false) && (notConnected==false)){ result=0; } 
				if (connectingInterrupted){
					// this condition appears, if a unique setting is attempted on a single machine
					// we should not break the loop, but redefine it!
				 
					delay(600);
					// redefining the unique setting
					// long providedID = factory.config().preferredID ;
					// out.print(2, "adjusted iid (a): "+providedID);
					// connectingInterrupted=false;
					result++;
					break;
				}
				
				if ((notConnected ) && (numberOfConnectRetry>1) && (z<numberOfConnectRetry)){
					String rcinfoStr = " ("+z+" of max. "+numberOfConnectRetry +")";
					if (numberOfConnectRetry<0){
						rcinfoStr = "" ;
					}
					out.print(2, "...registering failed"+rcinfoStr+", trying once more in a few seconds....\n");

					currentDelayBetweenConnectRetries = handlingReconnectDelay(z,numberOfConnectRetry, currentDelayBetweenConnectRetries);
					
				} else{
					xreg.setRegisteredSince( System.currentTimeMillis() ); 
					result=0;
				}
			} // while	notConnected, numberOfConnectRetry
			
			
		}catch(Exception e){
			e.printStackTrace();
		}
		if (xRegistration == null) {
			result = -3;
			if (connectingInterrupted==false){
				String str = "Failed to connect as "+mainRole+", even after "+numberOfConnectRetry+" trials.";
				throw (new Exception(str));
			};
		}else{
			factory.xRegistration = xRegistration ;
			
			factory.mbf = mbf ;
			
			// preparing the object for subscriptions
			futures = mbf.getFutures( xRegistration );
			// each registration has to know about itself which transport it is using
			// in case of dual MesssageBoard, we will have two different registrations
			// the same has to be mirrored on the side of the MessageBoard
			xRegistration.setProtocolID( this.factory.factoryProperties.getProtocolId() );
		} 
		
		return result;
		
	}
	
	public FuturesIntf getFutures(int mode){  // org.NooLab.glue.msgframe.XRegistration@1a3551c
		  
		// xRegistration provides a announce = publish method, as it knows about the DataConnector
		if (mode>0){
			
		}
		
		if (futures == null){
			out.print(2, this.toString()) ; 
			out.print(2, xRegistration.toString()) ;
		}
		
		return  futures;
	}
	
	public void setFutures(FuturesIntf  futures) {
		this.futures = futures;
	}

	public FutureIntf createFuture(){
		
		// we get & pass a handle to the global object for parameterizing similarity calculations
		// we also could request a copy (new instance!) of the DistanceControlProperties-object, stuffed with default values
		
		// empty,0 = default object, 1=copy
		DistanceControlProperties distanceCtrl = factory.getDistanceCtrlProperties() ;
		if (distanceCtrl==null){
			distanceCtrl = mbf.getCompareKernel(0).getDistanceCtrl();
		}
		
		FutureIntf future = futures.create(distanceCtrl);
 		
		return future;
	}

	public void establishConnections( String mainRole, InstanceFlavor flavor, int numberOfConnectRetry ) throws Exception{

		XRegistration xreg;
		boolean notConnected;
		int z=0, ticket = -1, currentDelayBetweenConnectRetries;

		// this waiting is bound to unique objects, so no problem with uniqueness so far
		// beyond this point, after entering mbf we WOULD have a problem, so we use a 
		// preemptive ticket system, issued by the collecting instance, the mbf object
		
		// the mbf hosts a process, which can take quite a long time
		
												out.print(2, "...connecting this instance as \""+mainRole+"\" for content of type \""+flavor.getContentType()+"\" to the message board...") ;
		
        try{
			ticket = mbf.getTicket();

			mbf.waitForTicket(ticket, 5);

			currentDelayBetweenConnectRetries = delayBetweenConnectRetries ;
			
			notConnected = true;
			while ( (notConnected) && 
					((z<numberOfConnectRetry) || (z<0))){
				
				if (mainRole.toLowerCase().contentEquals("source")) {
					try{
						connectSource(ticket, mainRole, -1, "", flavor,z);
						
					} catch(Exception e){  
						// educated silence...
					}
				}

				if (mainRole.toLowerCase().contentEquals("receptor")) {
					try{
						connectReceptor(ticket, mainRole,null, flavor);
					} catch(Exception e){  
						// educated silence...
					}
				}
				notConnected = (xRegistration == null);
				z++;
				
				if ((notConnected ) && (numberOfConnectRetry>1) && (z<numberOfConnectRetry)){
					
					String rcinfoStr = " ("+z+" of max. "+numberOfConnectRetry +")";
					if (numberOfConnectRetry<0){
						rcinfoStr = "" ;
					}
					out.print(2, "\n...registering failed"+rcinfoStr+", trying once more in a few seconds...\n");

					currentDelayBetweenConnectRetries = handlingReconnectDelay(z,numberOfConnectRetry,currentDelayBetweenConnectRetries);
					
				} else{
					xreg = this.getxRegistration();
					 
					if (xreg!=null){
						xreg.setRegisteredSince( System.currentTimeMillis() );
					}
				}

			} // -> notConnected = false

		}catch(Exception e){
			System.err.println("Instance of "+mainRole+" did not register and connect to Message Board!");
			
			if (xRegistration!=null){
				stop();
				xRegistration = null;
			}
		}


		if (xRegistration == null) {
			throw (new Exception("Failed to connect as "+mainRole+"."));
		}else{
			futures = mbf.getFutures( xRegistration );
		}
	}

	
	private int handlingReconnectDelay( int z, int numberOfConnectRetry, int currentDelayBetweenConnectRetries){
		boolean hb;
		
		delay( currentDelayBetweenConnectRetries);
		
		currentDelayBetweenConnectRetries = (int)Math.round(currentDelayBetweenConnectRetries * this.delayProgressionFactor) ;
		if (currentDelayBetweenConnectRetries > maxDelayBetweenReconnects ){
			currentDelayBetweenConnectRetries = maxDelayBetweenReconnects ;
			
		}
		
		hb = false;
		double c = (double)((double)z / (double)numberOfConnectRetry);
		hb = (numberOfConnectRetry>0) && ( c > 0.32) ;
		
		if ((hb) || (currentDelayBetweenConnectRetries > (double)(maxDelayBetweenReconnects/10.0))){
			double f = ((double)((double)currentDelayBetweenConnectRetries/(double)maxDelayBetweenReconnects)/1.0);
			           // [0.17 .. 0.5]
			f = (f+(2.0*c))/5.0;
				
			f = (delayProgressionFactor-1.0)*f;
			delayProgressionFactor = delayProgressionFactor - f ;
			if (delayProgressionFactor<1.0){ delayProgressionFactor=1.0;}
			
			f = ((double)Math.round( delayProgressionFactor*100.0 ))/100.0;
			
											if (f != delayProgressionFactor){
												out.print(3,"adjusted delayProgressionFactor: "+delayProgressionFactor+" -> "+f) ;
											}
			delayProgressionFactor = f;
		}
		return currentDelayBetweenConnectRetries;
	}
	
	protected void handlingOnStartupCompletion(){
		
		// if (hibernationActivated) // not only in case of hibernating clients... without the DeliveryPad, nothing will be returned
		{
			if (deliveryPad==null){
				deliveryPad = mbf.getDeliveryPad( this.participantsRole, xRegistration.getSuffixUID() ) ;
			}
		}	
		
		if (xRegistration != null){
			if ( mbf.getFactoryProperties().getProtocolId() >= MessageBoardFactoryProperties.protocolTCP ){
				if (participantsRole.toLowerCase().contains("receptor")==false){
					xRegistration.startWiredServer();
				}
			}
		}
		
		xRegistration.setReceptorState( XRegistration.RST_IS_AVAIL) ;
	}
	
	private void handlingTransactionCancellation( long transactionID){
		Transactions transactions;
		Transaction transaction;
		boolean hb;
		long tid;
		int d=0;
		
		upLinkProcedures.pause(1);
		
		transactions = mbf.getTransactions();
		
		
		for (int i=0;i<transactions.size();i++){
			transaction = transactions.getItem(i);
			tid = transaction.getTransactionID();
			hb = true;
			if (transactionID>0){
				hb = tid == transactionID;
			}
			if (hb){
				if ( (transaction.getTransactState()> Transaction.TS_TASK_ACCEPTED) &&
						 (transaction.getTransactState()< Transaction.TS_RESULTS_RETURNED)){
				    transaction.setTransactState(Transaction.TS_TASK_CANCELED);
				    out.print(3, "cancelation of transaction (tid:"+tid+") confirmed by MessageBoard, thus removing it here, too.");
				    d++;
				}
			}
		}
		transactions.save();
		
	}
	
	
	private void startUplinkService(){
		
		// if uplink checking should be started && if available
		if ( (useUplinkConnectPolling) && (upLinkService != null)){
			if  (msgBoardFactoryProperties.getParticipantsRole().toLowerCase().contains( MessageBoardFactoryProperties.variant_BSOURCE)==false ){				
				//
				// transferring the address of our procedures by its interface
				// "UpLinkConnectorIntf"
				upLinkService.setProcedureAddress(upLinkProcedures);
				this.upLinkService.start(); // the upLinkService knows about its factory
											// in SourcesUpLinkService.java 
				
			}
		}
	}
	
	
	protected void delayedReConnect( String participantsRole, boolean enforcedRestart ){
		
		int d = 40000;
		
		out.printErr(1, "Connected MessageBoard notified about its own shutdown... reconnect will be initiated in "+(int)(d/1000)+" seconds...");
		
		xRegistration.close();
		xRegistration=null;
		System.gc();
		
		delay(d);
		
		connect(participantsRole,false);
	}
	
	/**
	 * 
	 * this handles registration attempts which failed;
	 * those include such attempts for which the uniqueness could not be satisfied by the MessageBoard
	 * 
	 * circular course, if the original course is broken this becomes null
	 * 
	 * @param providedID
	 */
	private void handlingRegistrationRestart( long providedID){
		
		String uuid="" ;
		XRegistration xreg;
		Registration regengine;
	 
			
		// if (hibernationActivated){
			
		
		if (providedID<0){
			// registration failed with reason "id", but we did not get a suggested ID, so
			// as a last retreat we switch off uniqueness (for this session)
			// ...as a complication, this may lead to loss of transactions if we have to restart,
			//    and would get a different ID upon restart...
			factory.config().instanceIsUnique = false;
			connect(  participantsRole, false) ;
			
		}else{
			// with a new id we still can run uniqueness, ...
			// but we should do so ONLY if we are not on the same machine as the unique one, we should not overwrite an existing hibernation
			// -> check hibernation file, otherwise we would lose the track for the next startup
			
			// is there a hibernated registration ?
			/*
			regengine = new Registration (mbf, participantsRole); 
			xreg = regengine.loadRegistration( name, instanceIsUnique ); 
			
			if (xreg!=null){
				uuid = xreg.getSuffixUID() ;
				// appname = xreg.getNameOfApplicant() ;
				
				// str = factory.instance
				if (uniqueAdHocIdentifier.contentEquals(uuid)){
					
					factory.config().setInstanceAsUnique(false); 
					hibernationActivated = false;
					refuseHibernation = 1;
				}
			}
			*/
			factory.config().preferredID = providedID;
			connect(  participantsRole, true) ;
		}
		
		
	
	}
	
	 
	public void connect(String participantsRole, boolean enforcedRestart){
		
		boolean hb=false, ok, uniqueness;
		XRegistration xreg;
		Registration registration ;
		String str;
		long preferredId=-1;
		int r; 
		
		// if (participantsRole.contentEquals("SOURCE")==false ){ hibernationActivated=false; }
 		// hibernationActivated;
		// by default, always on, even for receptors...
		// yet, we distinguish unique and non-unique participants 
 		
		if (isCurrentlyInConnectingProcess){
			// simple mechanism to prevent overlapping connect attempts
			// but we should return only after completion, otherwise a non-connect would be detected
			while(isCurrentlyInConnectingProcess){
				delay(2);
			}
			return;
		}
		isCurrentlyInConnectingProcess = true;
		
		if (enforcedRestart == false) {
			if (xRegistration != null) {
				hb = true;

				if (hb) {
					hb = xRegistration.getActiveport() > 0;
				}
				if (hb) {
					hb = xRegistration.getClientListeningPort() > 0;
				}

			}
			if (hb) { // already registered, and registration knows about its own port

				xRegistration.setAcceptType(flavor.contentType);

				startUplinkService(); // starts only if not started yet
				
				isCurrentlyInConnectingProcess=false;
				return;
			}
		}
		
		preferredId = factory.config().preferredID;
		uniqueness = factory.config().getInstanceIsUnique() ;
		
		if (((preferredId<=0) && (uniqueness==false))){
			hibernationActivated = false;
		}
		// flavor.setInstanceIsUnique(false) ;
		
		// TODO: MOVE -> should be loaded on startup...
		if (hibernationActivated){ // ??? || ((enforcedRestart) && (xRegistration!=null))
			
			// get a registration object
			registration = new Registration (mbf, participantsRole); 
			xreg = registration.loadRegistration( name, instanceIsUnique ); // uses name and serialID for filename
			// does not work
			 
			// if xreg remains null, the xRegistration will be created by method "readConfirmedRegistration()",
			// after receiving the receipt from the MessageBoard
			
			// now register @ MessageBoard using the data from previous xRegistration, which we have just loaded
			if (xreg!=null){
				// get flavor object filled
				this.uniqueAdHocIdentifier = xreg.getSuffixUID() ;
				if ((instanceIsUnique==false) && (uniqueAdHocIdentifier.length()==0)){
					uniqueAdHocIdentifier = GUID.randomvalue() ;
				}
				flavor.setConnectionDelay( xreg.getConnectionDelay()) ;
				flavor.setLinkRelation( xreg.getSrlinkRelation() ) ;
				flavor.setRelationGroupID( xreg.getSrRelationGroupID() ) ;
				flavor.setRelationItemID( xreg.getSrRelationItemID() ) ;
				flavor.setInstanceIsUnique(instanceIsUnique) ;
				
				ok=true;
			}else{
				// =======================
				// create a new one: hibernation requested, name known, and prob. even the preferred ID of the instance
				xreg = new XRegistration();
				xreg.setOutPrn(out);
				xreg.setSuffixUID(uniqueAdHocIdentifier);
				xreg.setConnectionDelay(0.0d);
				
				xreg.setSrlinkRelation( flavor.getLinkRelation()) ;
				xreg.setSrRelationGroupID( flavor.getRelationGroupID());
				xreg.setSrRelationItemID( flavor.getRelationItemID() );
				
				flavor.setConnectionDelay( xreg.getConnectionDelay()) ;
				flavor.setLinkRelation( xreg.getSrlinkRelation() ) ;
				flavor.setRelationGroupID( xreg.getSrRelationGroupID() ) ;
				flavor.setRelationItemID( xreg.getSrRelationItemID() ) ;
				flavor.setInstanceIsUnique(false) ; // could be revised just a bit down
				ok=true;
			}	
			
			if (ok){
			
				if (factory!=null){
					
					if (preferredId > 0) {
						xreg.setInstanceID(preferredId) ;
						instanceIsUnique = true ;
					}
				}
				// connect with the loaded registration
				try {
					xreg.setRole(participantsRole) ;
					str = xreg.getNameOfApplicant() ;
					
					flavor.setInstanceIsUnique(instanceIsUnique) ;
					xreg.setInstanceIsUnique(instanceIsUnique);
					
					if ((str.toLowerCase().contains( "-"+participantsRole.toLowerCase()  )==false) ||
					    (str.contains(uniqueAdHocIdentifier)==false )){
						if (uniqueAdHocIdentifier.length()<10){ uniqueAdHocIdentifier = GUID.uniquevalue();}
						str = name.toLowerCase()+"-"+participantsRole.toLowerCase()+"-1-" + uniqueAdHocIdentifier  ;
						
						xreg.setNameOfApplicant("") ; // we would get it from the MessageBoard (?)
						xreg.setNameOfApplicant( str ) ;
					}
					 
					establishConnections( xreg, flavor, numberOfConnectRetry ) ; // here we pass the xreg object...
					 
					// again: providing the loaded getSuffixUID, since this xRegistration-object
					// is a new one (freshly assigned ports, etc.) the loaded object is not used any further
					// this is the final step to transfer settings from hibernated xreg object to the living one!
					
					if (xRegistration!=null){
						
						xRegistration.setSuffixUID(uniqueAdHocIdentifier);
						xRegistration.setRegisteredSince(System.currentTimeMillis());

						if (refuseHibernation <= 0) {
							// String filename =
							registration.saveRegistration(name, xRegistration, 0);
						}
						startUplinkService();
						handlingOnStartupCompletion();

					}else{ 
						
						if (connectingInterrupted){
							refuseHibernation = 1 ;
							restartConnect( participantsRole, enforcedRestart);
							return;
						}
						this.mbf = null;
						if (upLinkService!=null){
							upLinkService.stop();
						}
						// we simply return to the surface layer, where one can decide to try to reconnect
					}
				} catch (Exception e) {
					str="";
					if (e.getMessage()!=null){str=e.getMessage();}
					str = "Exception in connect(a): "+str;
					String _role = mbf.getFactoryProperties().getParticipantsRole().toLowerCase();																		
							   	        // "iboard"
					if (_role.contains( MessageBoardFactoryProperties.variant_BSOURCE)==false){
						out.printErr(2, str);
						out.printErr(2, "Thus shutting the instance down.");

						System.exit(7) ;
					}
				}
				 
				registration = null; // temp xreg object	
				 
				// xRegistration.sendKeepAlive() ;
				isCurrentlyInConnectingProcess=false;
				return;
			} // xreg!=null ?
			
			registration = null;	
			
		} // hibernationActivated ?
		
		// values >0 cause polling of participants, instead of sending messages by the MessageBoard
		flavor.setConnectionDelay( 2.5 ) ;
		try{
			flavor.setLinkRelation(2);
			
			try{
				establishConnections( participantsRole, flavor, numberOfConnectRetry) ; // and here not...
				 
			}catch(Exception e){
				out.printErr(2, e.getMessage()) ;
				out.printErr(2, "Thus shutting the instance down.") ;
				System.exit(7) ;
			}
			// the primary receiver now is connected to an Observer-update in DataConnector
			// it is also referred to an object xRegistration

			// filters must be activated explicitly if they shall be used
			// msgboardFactory.setFilterActivation( false ) ;  
			
			// if transactions should not be saved temporarily (for resistance against interruptions)
			// xRegistration.setTransactionPersistence( false ); // default value = true
			if ((xRegistration != null) && (xRegistration.getNameOfApplicant().length()==0)){
				xRegistration.setNameOfApplicant( name );
			}
			
			// not to forget saving the "suffixUID" to the final registration object,...
			// we need it for saving the xreg !
			// it has been set to the catalog value: xreg.getSuffixUID(), if that was available...
			if ( uniqueAdHocIdentifier.length()>0 ){
				xRegistration.setSuffixUID(uniqueAdHocIdentifier);
			}
			if (xRegistration.getRole().toLowerCase().contains("source")){
				// work through left overs from previous runs, where we 
				// did not receive the results in the same session
			}

			if (xRegistration!=null){
				registration = new Registration (mbf, participantsRole);  // a registration object without params for those simple tasks?
				registration.saveRegistration( name, xRegistration,0 );
				registration=null;
				xRegistration.setRegisteredSince( System.currentTimeMillis()) ;
			
			 
				handlingOnStartupCompletion();
			
				// if httpc => for receptors: useUplinkConnectPolling, upLinkService
				if (upLinkProcedures==null){
					upLinkProcedures = new UpLinkConnector();
				}
				startUplinkService() ; 
			}else{
				if (connectingInterrupted){
					restartConnect( participantsRole, enforcedRestart);
					return;
				}
			}
			 
		}catch(Exception e){
			str = "Exception in connect(b): "+e.getMessage();
			out.printErr(2, str ) ;
			System.err.println("Instance of "+participantsRole+" did not connect and register to Message Board!");
		}
		finally{
			isCurrentlyInConnectingProcess=false;
		}
		
		
	}
	
	public void restartConnect( String participantsRole, boolean enforcedRestart ){
	
		long providedID = factory.config().preferredID ;
											out.print(4, "adjusted iid (b): "+providedID+" ...  re-connecting");
		//hibernationActivated=false;
		// only on the second failure we drop even uniqueness and hibernation

		isCurrentlyInConnectingProcess = false;
		connectingInterrupted=false;

		connect(participantsRole, enforcedRestart);

		factory.xRegistration = xRegistration;

    }
	
	
	private void connectSource( int ticket, String mainRole, long instanceID, String applicantName, InstanceFlavor flavor, int attemptNo  ) throws Exception{
		
		DataReceiverIntf recvForRegistration ;
		XRegistration xregS = null, xregR=null;
		
		mrd  = new MessageReceptorDigester(mbf,this) ;
		
		
		
		// main role: SOURCE, or IBOARD
		if (MessageBoardFactoryProperties.isSourceType(mainRole)){
			
			// TODO: in MessageBoard upon failure, remove any entry in the list of registered instances, using the name !!
			
			
						RegisterParamPackage rpp = mbf.getRegisterParamPackage();
						rpp.regTicket = ticket; 
						rpp.name = name ; 
						rpp.instanceID = instanceID ; 
						rpp.applicantName = applicantName ; 
						rpp.flavor = flavor;
						rpp.resultsReceiver = mrd ;
						rpp.attemptNo = attemptNo;
						
			xregS = mbf.attachSource( rpp ) ;
			// this xRegistration instance is a mirror (!) of the one, which lives in the MessageBoard
			// problem in openReceptor: xRegistration will be reset 
			
			recvForRegistration = mbf.getComBoxFactory().getReceiver();
			 
			ComBox cb = mbf.getComBoxFactory().getComBox();
			if (cb!=null){
				cb.removeReceiver( recvForRegistration ) ;
			}
			  
		}
		
		// main role: SOURCE, registering the receptor part, if we have a two way instance
		if ( (xregS!=null) && 
			 ( MessageBoardFactoryProperties.isSourceType(mainRole)   && 
			 ( numutil.isOdd( flavor.getLinkRelation())==false ))){
			
			// StringedObjects strgObj = new StringedObjects();
			// xRegistration = (XRegistration)(strgObj.decode( strgObj.encode(xregS) ));
			// if doing the above, we have to recreate the objects, namely the DataConnector
			
			xRegistration = xregS;
			
			mrd.setSourceRegistration(xRegistration);
			
			upLinkProcedures = new UpLinkConnector();
			
			/*
			the duality should NOT be established within another participant deep level, instead, the whole participant 
			should be created a proper facade
			 */
			if (msgBoardFactoryProperties.getInstanceBivalence()){
				mrd.setTicketValue(ticket) ;
				
				mrd.initializeMessageBoardConnection(0,null);
			}
			
		}
		
		 
		
		if (xregR == null){
			// xRegistration = xregS; already done, we are working with obj references
		}
		 
		
	}

	private void connectReceptor( int ticket, String mainRole, XRegistration xreg, InstanceFlavor flavor  ){
		
		mrd  = new MessageReceptorDigester(mbf, this) ;
		mrd.setTicketValue(ticket) ;
		
		mrd.initializeMessageBoardConnection(1,xreg);
		
		if (xRegistration!=null){
			upLinkProcedures = new UpLinkConnector();
		}
		// main role: RECEPTOR, registering the source part
		if ( (xRegistration!=null) && (mainRole.toLowerCase().contentEquals("receptor")) && (flavor.getLinkRelation())>=1){
			// create a DataConnector for sending;
			// it is created in the ini() of XRegistration
												out.print(4, "connectReceptor for '"+mainRole+"': xRegistration.init(), ID = "+xRegistration.getInstanceID()+", "+mainRole+"'s listening port: "+xRegistration.getClientListeningPort());
			xRegistration.init ( mbf, mrd ) ;
			// instanceID, String applicantName
		}
		
	}
	
	abstract public void currentStateMessage(String str) ;

	abstract public TaskPackage digestingReceivedMsg( Vector<TaskPackage> taskPackages ) ;

	abstract public TaskPackage digestingReceivedMsg(  TaskPackage taskPackage  ) ;
	
	abstract public void connect();
  
	abstract public void disconnect();

	 
	private Vector<TaskPackage> getAllResultPackagesToTid( long transactionID ){
		
		String filename ;
		 
		long tid, iid;
		MsgBoardDataStorage  mbstorage;
		
		ResultCompletionTrigger rtc;
		Vector<TaskPackage> _resultPackages = new Vector<TaskPackage>() ;
		TaskPackage tp   ;
		
		
		mbstorage = msgboardFactory.getMessageBoardStorage();
		iid = xRegistration.getInstanceID();
		
		try{
			
			for (int i=0;i<resultCompletionTriggers.size()-1;i++){
				rtc = resultCompletionTriggers.getItem(i);
				tid = rtc.transactionID; 
				tp = null;
				// load it from file, if available, the filename guarantees the correct return, even if there are
				// several source instances on the same machine
				 
				filename = mbstorage.createParticipantsResultBufferfileName( iid, tid );
				
				if (fileutil.fileexists(filename)) {
					
					tp = (TaskPackage) mbstorage.readTransactionDataObj(filename);

					if (tp != null) {
						_resultPackages.add(tp);
					}
					fileutil.deleteFile(filename);
				}
			}
		}catch(Exception e){
			
		}
		
		
		return _resultPackages;
	}
	
	
	
	/**
	 * 
	 * @param localResultPackage
	 */
	private void deliveringReceivedMsg( TaskPackage localResultPackage ){
		long tid ;
		int ix;
		String filename;
		  
		Vector<TaskPackage> _resultPackages = new Vector<TaskPackage>() ;
		MsgBoardDataStorage mbstorage;
		 
		
		tid = localResultPackage.getTransactionID() ;
		
												out.print(3, "deliveringReceivedMsg(), transaction tid="+tid);
		
		if ((collectResults==false) || (resultCompletionTriggers.size()==0)){
		
			// all buffered results up the tid of this transaction: buffering took place upon previous non-matches
			// buffers are handled by class MessageBoardStorage
			 
			
			// if nothing exists it returns an empty collection
			_resultPackages = getAllResultPackagesToTid(tid);
			
			// and finally the latest one
			_resultPackages.add(localResultPackage) ;
			
			// finally lifting to the presentation layer
			digestingReceivedMsg( _resultPackages ) ;
			
			return;
		}
		
	
		// check whether this one matches
		ix = resultCompletionTriggers.isMatching( localResultPackage );
		
												
		// if yes, prepare collection and deliver
		if (ix>=0){
			long triggeredTid = resultCompletionTriggers.getItem(ix).transactionID;
			
												out.print(2, "deliveringReceivedMsg(), match detected for trigger (ix:"+ix+",triggered tid="+triggeredTid+") and results-tid="+tid+", trigger will be removed");
												
			// get all buffered result packages up to this id
			// this index contains the latest result also...
			
			_resultPackages = getAllResultPackagesToTid(tid);
			
			_resultPackages.add( localResultPackage );
			
			resultCompletionTriggers.removeItems( 0, ix) ;
			
			digestingReceivedMsg( _resultPackages );
			
			return;
			
		} else {
			// if no match, store it into the local buffer,
			
			if (ix!=-3){
				// resultCompletionTriggers is empty, show it !
												out.print(2, "deliveringReceivedMsg(), list of triggers presumed to be empty, thus immediate delivery... " );
				_resultPackages.add(localResultPackage) ;
				 
				digestingReceivedMsg( _resultPackages ) ;
			
				resultCompletionTriggers.removeItemByTID( localResultPackage.getTransactionID() ) ;
				
				return;
				
			}else{
				// but only if the result trigger array has an entry at all (ix!=3)
												out.print(2, "deliveringReceivedMsg(), no match detected, number of triggers="+resultCompletionTriggers.size() );
												
				mbstorage = msgboardFactory.getMessageBoardStorage();
				 
				// mbstorage.createTransactionDataObjTmpFilename(transactID, instanceID)
				filename = mbstorage.createParticipantsResultBufferfileName( xRegistration.getInstanceID(), localResultPackage.getTransactionID() );
				
				mbstorage.storeTransactionDataObj(localResultPackage, filename);
				
				completedResultBuffers.add(filename);
			}
		
		}
		
	}
	
	@Override
	public int getStatus() {
		 
		return 0;
	}
	
	
	protected void stop(){
		
		int p,  port;
		long iid;
		String role, portinfo="";
		Registration registration ;
		XRegistration xreg;
		
		if (xRegistration==null){
			return;
		}
		
		upLinkProcedures.pause(10);
		
		if (upLinkService!=null){
			upLinkService.stop() ; 
		}
		// we have to buffer the current xreg object, since the new instance of Registration creates its own/a new one
		xreg = xRegistration;
		iid = xreg.getInstanceID() ;
		
		// create & send unregister messages, containing
		// <register><unregister>
		port = xRegistration.getClientListeningPort() ;
		role = xRegistration.getRole() ; 
		
		if (hibernationActivated){
			
			registration = new Registration ( mbf,participantsRole ); 

			if (instanceIsUnique){
				registrationStoragefilename = registration.saveRegistration( name, xreg,1 ); // 1=is closing 
			}
			
			// using name (e.g. "testSource0" i.e. without GUID) and SerialID of XRegistration object
			registration = null;
			// also save open transactions, using 
			 
			mbf.saveOpenTransactions(); // these are contained in a simple map <transactionID, privateSecetStr>
			
			if (deliveryPad!=null){ // if pure SOURCE, if NOT hibernation
				deliveryPad.close();
			}
		}
	
		
		
		p = mbf.getReservedPorts().indexOf(port);
		if (p>=0){
			mbf.getReservedPorts().remove(p) ;
		}
		
		xRegistration.unregister();
		xRegistration = null;
		
		System.gc() ;
		
		if (mbf.getFactoryProperties().getProtocolId()>= MessageBoardFactoryProperties.protocolTCP){
			portinfo = " (port:"+port+")";
		}
		out.printErr(2, "Instance of role "+role+", id="+iid+" unregistered itself"+portinfo+" from MessageBoard."); 
	}

	protected int cancel(){
		
		return this.cancel(-1) ;
	}

	protected int cancel( long transactionID){
		long tid;
		
		if (transactionID<=0){
			// the collection transactions is not up-to-date !! actually, the last one is missing
			tid = this.mbf.getTransactions().getLastTransactionID() ;
			
			transactionID = tid;
			if (tid<0){
				return -21;
			}
		}
		
												out.printErr(2, "canceling task (tid:"+transactionID+")...");
		return xRegistration.cancelTask(transactionID) ;
	}
	
	protected void cancelAll(){
	
		xRegistration.cancelAll();
		
	}
	
	protected void retrieveAll(){
		
	}
	
	protected void unregister( ){
	
		upLinkProcedures.pause(10);
		
		if (xRegistration!=null){
			mbf.detachSource( xRegistration ) ;
		}
	}
		
	@SuppressWarnings("unused")
	private void unregister( XRegistration xreg, InstanceFlavor flavor){	
		
		upLinkProcedures.pause(10);
			
		mbf.detachSource(  xreg, flavor ) ;
	}
	
	
	protected void setTransactionState( long transactionID, int stateValue ){
		Transactions transactions;
		Transaction transaction;

		transactions = mbf.getTransactions() ;
		transaction = transactions.getTransactionByID(transactionID) ;
		
		if (transaction!=null){
			transaction.setTransactState( Transaction.TS_TASK_CANCELED ) ;
		}
		// there will be an independent process dealing with the list of transactions and cleaning it. 
		
	}


	public TaskPackage getNewTaskPackage(){ 
		TaskPackage taskPackage = null;
		
		taskPackage = new TaskPackage(); 
		taskPackage.setxRegistration(xRegistration);
		
		return taskPackage ;
	}
	
	public TaskPackage getNewTaskPackage( String contentType, boolean transmitResourceAsRef ){
							     // e.g. "rawdata", specialContentType, false ) ;
		TaskPackage taskPackage = null;
		
		taskPackage = new TaskPackage(); 
		taskPackage.setxRegistration(xRegistration);
		
		taskPackage.setTransmitResourceAsReference(transmitResourceAsRef) ;
		taskPackage.setContentType( contentType ) ;
		 
		
		return taskPackage ;
	}
	
	 
	   
	public void setContentType( String ctype){
		
		specialContentType = ctype;
		if (taskPackage!=null){
			taskPackage.setContentType(specialContentType) ;
		}
		flavor.setContentType(specialContentType) ;
	}
	
		
	public InstanceFlavor getFlavor() {
		return flavor;
	}




	public void setFlavor(InstanceFlavor flavor) {
		this.flavor = flavor;
	}




	public String getSpecialContentType() {
		return specialContentType;
	}



	/**
	 * "ContentType" is a String and could be any, while * is the wildcard FOR any;
	 * 
	 * @param specialContentType
	 */
	public void setSpecialContentType(String specialContentType) {
		this.specialContentType = specialContentType;
		flavor.setContentType(specialContentType) ;
	}




	public String getName() {
		return name;
	}




	public void setName(String name) {
		this.name = name;
		flavor.setName(name) ;
	}

	
	public void activeReceiptsExchange( boolean b){
		int currentLinkRel;
		
		if (flavor.getLinkRelation()<=0){
			flavor.setLinkRelation(1) ;
		}
		currentLinkRel = flavor.getLinkRelation() ;
		 
		/*
		 * all odds : 1,3,5 = no feedback to SOURCE
		 * all evens: 2,4,6 = with feedback to SOURCE
		 */
		if (b){
			currentLinkRel=numutil.nextEvenNumber(currentLinkRel) ;
		}else{
			currentLinkRel=numutil.prevOddNumber(currentLinkRel) ;
		}
		
		if (currentLinkRel<=0){
			currentLinkRel=1 ;
		}
		flavor.setLinkRelation(currentLinkRel) ;
	
	}

	public boolean isInstanceIsUnique() {
		return instanceIsUnique;
	}

	public void setInstanceIsUnique(boolean instanceIsUnique) {
		this.instanceIsUnique = instanceIsUnique;
	}

	public String getParticipantsRole() {
		return participantsRole;
	}

	public void setParticipantsRole(String participantsRole) {
		this.participantsRole = participantsRole;
	}

	public boolean isHibernationActivated() {
		return hibernationActivated;
	}

	public void setHibernationActivated(boolean hibernationActivated) {
		this.hibernationActivated = hibernationActivated;
	}

	public int getNumberOfConnectRetry() {
		return numberOfConnectRetry;
	}



	public void setNumberOfConnectRetry(int numberOfConnectRetry) {
		this.numberOfConnectRetry = numberOfConnectRetry;
	}



	public int getDelayBetweenConnectRetries() {
		return delayBetweenConnectRetries;
	}



	public void setDelayBetweenConnectRetries(int delayBetweenConnectRetries) {
		this.delayBetweenConnectRetries = delayBetweenConnectRetries;
	}



	public double getDelayProgressionFactor() {
		return delayProgressionFactor;
	}



	public void setDelayProgressionFactor(double delayProgressionFactor) {
		this.delayProgressionFactor = delayProgressionFactor;
	}



	public int getMaxDelayBetweenReconnects() {
		return maxDelayBetweenReconnects;
	}



	public void setMaxDelayBetweenReconnects(int maxDelayBetweenReconnects) {
		this.maxDelayBetweenReconnects = maxDelayBetweenReconnects;
	}



	public boolean isUseUplinkConnectPolling() {
		return useUplinkConnectPolling;
	}

	public void setUseUplinkConnectPolling(boolean useUplinkConnectPolling) {
		this.useUplinkConnectPolling = useUplinkConnectPolling;
	}

	public int getUseUplinkConnectPollingPeriodLength() {
		return useUplinkConnectPollingPeriodLength;
	}



	public void setUseUplinkConnectPollingPeriodLength(
			int useUplinkConnectPollingPeriodLength) {
		this.useUplinkConnectPollingPeriodLength = useUplinkConnectPollingPeriodLength;
	}



	public boolean isDropDeniedTasks() {
		return dropDeniedTasks;
	}

	public void setDropDeniedTasks(boolean dropDeniedTasks) {
		this.dropDeniedTasks = dropDeniedTasks;
	}

	public XRegistration getxRegistration() {
		return xRegistration;
	}

	 

	public void setTaskPackage(TaskPackage taskPackage) {
		this.taskPackage = taskPackage;
	}

	public MessageReceptorDigester getMrd() {
		return mrd;
	}

	public MessageBoardFactory getMsgboardFactory() {
		return msgboardFactory;
	}

	public ParticipantUpLinkServiceIntf getUpLinkService() {
		return upLinkService;
	}

	public void setUpLinkService(ParticipantUpLinkServiceIntf upLinkService) {
		this.upLinkService = upLinkService;
	}

	public UpLinkConnectorIntf getUpLinkProcedures() {
		return upLinkProcedures;
	}

	public void setOut(PrintLog out) {
		this.out = out;
	}

	@SuppressWarnings("static-access")
	protected void delay( int millis ){
		int  steps;
		long startTime = System.currentTimeMillis() ;
		
		if (millis>1000){
			try {
				steps = (int) Math.round(((double) (millis)) / ((double) (100.0)));

				for (int i = 0; i < steps; i++) {
					Thread.currentThread().sleep(100);
					if (startTime+millis<System.currentTimeMillis()){
						break;
					}
				}
			} catch (InterruptedException e) {
			}
		} else {
			try {
				Thread.currentThread().sleep(millis);

			} catch (InterruptedException e) {
			}
		}
	}

	abstract public void handlingCancelRequest( TaskPackage taskPackage, int marker );
	
	public boolean getBusyState(){
		
		boolean rB=false;
		if (upLinkProcedures!=null){
			rB = upLinkProcedures.getBusyFlag() ;
		}else{
			rB=false;
		}
		return rB;
	}

	public void setBusyFlag(boolean value){
		
		if (upLinkProcedures!=null){
			upLinkProcedures.setBusyFlag(value);
		}
	}
	 
	
	public void handlingFailedTransactions( TaskPackage taskPackage ){
		long transactionID=-1 ;
		int  sv=-1,esv=-1 ;
		String str="";
		// String pkgmsg = "";
		Transaction transaction=null ;
		Object obj ;
		
		
		if (taskPackage == null){
			
			out.printErr(2, "no special error handling possible, transaction will just be removed from memory.");
			return ;
		}
		
		
		try{
			obj = taskPackage.getDataObj();
			
			if (obj != null){
				
				transaction = (Transaction)obj ;
			}
		}catch(Exception e){
			
		}
		str = "";
		
		if (taskPackage != null){
			transactionID = taskPackage.getTransactionID() ;
			sv = taskPackage.getStateFlag() ;
			str = taskPackage.getDataObjIdentifier() ;
			// pkgmsg = taskPackage.getDataString();
		}
		
		
		if (transaction != null){
			transactionID = transaction.getTransactionID() ;

			// before this has been assigned: transaction.setDataObject( includedErrMsg );
			// which is a string
			obj = transaction.getDataObject() ;
			str = (String)obj ;

		} else{
			if (str.length()==0){
				str = "unknown." ; 
			}
		}
		
		if (transaction!=null){
			sv = transaction.getTransactState() ;
			esv = transaction.getTransactErrorState() ;
		}
		
		if ( ( (esv < Transaction.TS_ERR_NOMATCH) && (esv!= Transaction.TS_ERR_TIMEDOUT)) || 
		     ( (esv<0) && (sv<0) )){
			out.printErr(2, "\nhandling a failed transaction (id:"+transactionID+")... "+
					    	"state:"+sv+" ("+Transaction.decodeTransactionCode(sv)+"), errorcode:"+esv+" ("+Transaction.decodeTransactionCode(esv)+")"+
					    	"\nfurther details: "+str+
							"\n-> will just drop it.");// , so far...");
		}
		// now we can do anything with the postponed transaction ....
		// retry... drop it...
		mbf.getTransactions().removeItemByID(transactionID) ;
		mbf.getTransactions().save() ;
		
		// TODO: out the ID into a list, which is worked on by another thread, 
		// 		 this thread offers retry for n times after some time ...
		
		// TODO: the surface should be informed, too
	}
	
	/*
	 * if no string is provided, the last transaction ID will be taken
	 * if the marker is not satisfied before the last transactionID returns, the tid will be taken in order to avoid an abyss for the data
	 * the work is done in abstract class MessageBoardParticipant
	 */
	protected void setResultsCompletionMarker() { // later version: add support for string markers
		String marker="";
		ResultCompletionTrigger rct = null;
		
		if ( collectResults==false ){
			return;
		}
		
		// these transactions are different from those in 
		Transactions transactions = this.mbf.getTransactions() ; 
		 
												// out.print(4, "transactions object from mbf (MessageBoardParticipant): "+ transactions.toString() );
		
		
		if ((marker!=null) && (marker.length()>0)){
			rct = new ResultCompletionTrigger();
		}
		
		// wait a bit, actually we have to use a GUID !!!!! 
		int z=0;
		while ( (z<200) && 
				((xRegistration.getCurrentlySending()) || (xRegistration.getOpenRequestsForTransaction()>0)) || (xRegistration.getCurrentlyWaitingforTID()) ){
			
			if ( (xRegistration.getCurrentlySending()) || (xRegistration.getCurrentlyWaitingforTID())){
				
			}else{
				z++;
			}
			delay(10);
		}
		
		
				
		// after waiting a bit, we take the ID of the last transaction that has been successfully opened
		if (transactions.size()>0){
			Transaction t = transactions.getItem(transactions.size()-1);
			
			if (t!=null){
				if (rct==null){ rct = new ResultCompletionTrigger(); }
				
				long tid = t.getTransactionID() ;
				
				if (resultCompletionTriggers.indexByID(tid)<0){
		
					rct.setTransactionID( tid);
					out.print(2,"The id of the last transaction (tid:"+tid+") opened and confirmed by the MessageBoard has been recorded...   \n");
				}
			}
		}
		
		if (rct!=null){
			// we simply overwrite the last trigger
			resultCompletionTriggers.add( rct );
			// it will be removed on its match
		}
		
		
		return ;
	}
	
	protected void retrieveAll( String contentType){
		xRegistration.retrieveAll(contentType);
	}
	
	protected void send(){
		
		xRegistration.send(taskPackage, null,0) ;
	}
	
	protected void send(TaskPackage taskPackage){
		
		// the registration object knows about the next level method for sending,
		// which is represented by the "DataConnector{}";    
		// this "send" is just a facade! 
		xRegistration.send(taskPackage, null,0);
		
	}
	
	protected void send(TaskPackage taskPackage, FilterPackageIntf filter, int routableHops ){
		
		// the registration object knows about the next level method for sending,
		// which is represented by the "DataConnector{}";        
		// this "send" is just a facade!   
		xRegistration.send(taskPackage, filter, routableHops);
		
	}

	protected void send( String xmlout, int routableHops, Observer obs ){
		
		if (routableHops<=0){
			xRegistration.directSend(xmlout,obs);
		} else {
			xRegistration.send(xmlout, routableHops,obs);
		}
		
	}
	
	/** here we are in the role of a RECEPTOR which is returning results to the board...  the board should know about the transaction... */
	protected void sendResultPackage(){
		int linkrel, sv;
		long transactID;
		boolean tranStateOK;
		
		String localDeliveryReceipt;
		
		
		
		if (this.participantsRole.toLowerCase().contains("source")){
			out.printErr(3, "access to <sendResultPackage> denied for SOURCE");
			return;
		}
		
		if (mbf.getComBoxFactory().getProtocolID()<MessageBoardFactoryProperties.protocolTCP){
			
		}
		
		transactID = mTransact.getTransactionID();
		sv = mTransact.getTransactState();
		tranStateOK = (sv>=Transaction.TS_TASK_ACCEPTED) && ((sv<Transaction.TS_TASK_CANCELED) || (sv> Transaction.TS_TASK_UNKNOWN) ) ;
			
		linkrel = xRegistration.getSrlinkRelation();
		if ( (tranStateOK) && (linkrel>0) && ( numutil.isOdd(linkrel)==false)){
			
			// we should not send directly... instead, we should put it to an object,
			// which maintains a queue of results, ...that queue also may hibernate on itself...
			
			// the situation is much the same as for the MessagebOard, trying to send results to the SOURCE 
			
			// previously: if NOT hibernation -> deliveryPad = null 
			if (deliveryPad!=null){
				// defined in ParticipantsDeliveryPad{}, but only accessible through intf, created by MessageBoardFactory during startup
				if (resultPackage!=null){
					localDeliveryReceipt = deliveryPad.createNotificationForMessageBoard( xRegistration ) ;
					
					// the actual sending will be performed through  xRegistration.sendResultReturn()
					deliveryPad.put( localDeliveryReceipt, transactID, resultPackage ) ;
												out.print(2, "\nSending results (tid:"+transactID+") to MessageBoard using deliveryPad ...");
				} else{
												out.printErr(2, "\nNothing returned to MessageBoard, since resultPackage is NULL.");
				}
			} else {
				// send it directly ???
				out.printErr(2,"No sending possible, since the sending device ('DeliveryPad') is down.") ;
			}
			// from there it is taken for sending , including several attempts, e.g. after re-starting
			// including confirmation handshake with the target
			// once it is on the deliveryPad we (the participant) can forget about it
			
		} else{
			// some error, like timed out or canceled by the SOURCE ... 
			// just doing nothing...
		}
	}
	private void sendReceptorsDeliveryConfirmationtoBoard( Transaction transaction){

		// long tid;
		// tid = transaction.getTransactionID() ;
		
		xRegistration.sendConfirmationToBoard( transaction, Transaction.TS_TASK_DIGESTING) ;
		
	}
	
	
	protected void sendDigesterStateMsg( int stateValue, long transactID, String description  ){
		 
		int linkrel;
		long instanceID;
		String role;
		
		linkrel = xRegistration.getSrlinkRelation();
		instanceID = xRegistration.getInstanceID() ;
		
		if ((linkrel>0) && ( numutil.isOdd(linkrel)==false) && (instanceID>0)){
			role = xRegistration.getRole() ;
			xRegistration.sendStateToMsgBoard( instanceID, transactID,role, stateValue, description) ;
		}
		
	}

	protected void sendStateMessageForMessageBoard( int stateValue, String rawXmlStr, String description){
		long instanceID;
		
		long transactionID;
		Transaction mirrTransaction ;
		
		
		XMessage xMessage = new XMessage(out);
		
		mirrTransaction = xMessage.readReceptorTransaction(rawXmlStr);
		transactionID = mirrTransaction.getTransactionID();
		
		instanceID = xRegistration.getInstanceID() ;
		
		xMessage=null;
		xRegistration.sendStateToMsgBoard( instanceID, transactionID, xRegistration.getRole(), stateValue, description) ;
	}



	protected void sendRequestForAliveNotifications(){
		
	}
	
	protected void sendEchoResponse( String xmlstr){
		
		xRegistration.sendEchoResponse( name, xmlstr );
	}



	/**
	 * 
	 * not in use so far...
	 * 
	 * this is only used for asynchronous transports
	 * @param instanceID
	 * @param role
	 */
	protected int sendRequestForTaskPackageResource( long instanceID, String role ){
		int result = 0;
		// create a neat XML and send it... wait for the resource, and GET it
		
		// as active RECEPTOR we should issue a privacy marker (UUID string)
		
		
		String rawXmlMsg ="", contentType;
		// long iid, tid ;
		// String secret;
		
		// secret = "" ; TODO ...
		
		if (xRegistration==null){
			return -1;
		}
		
		XMessage xMessage = new XMessage(out);
		
		contentType = flavor.contentType ;
		
		// this should exclude the last transaction id !
		// at least the last treated transaction should be denied, in order to avoid
		// unintentional reloading
		rawXmlMsg = xMessage.createRequestForTaskResource( instanceID, excludedTransactionIDs, role, contentType ) ;
		 
		 
		xRegistration.sendRequestPackage( rawXmlMsg )  ;
		// it will be introduced first to: "handling_ReceivedResourceKeyForTaskDownload()", then to "handlingRoutedTransactions()"
		return result;
	}
	

	
	protected void sendRequestForResultStateResource( Transaction transaction , String role){
		
		String rawXmlMsg ="";
		long iid, xid,tid ;
		String secret;
		Transactions ts;
		
		XMessage xMessage = new XMessage(out);
		
		// there should be only transactions issued by our own instance ID ...
		//  already filtered by load() in Transactions...
		// yet, we check again here ; 
		xid = xRegistration.getInstanceID() ;
		
		iid = transaction.getInstanceID() ;
		tid = transaction.getTransactionID() ;
		secret = transaction.getClientSecret() ;
		
		if (iid != xid){
			return;
		}

		if ((transaction.getTransactState()>= Transaction.TS_TASK_CANCELED) &&
			(transaction.getTransactState()<= Transaction.TS_TASK_UNKNOWN)) {
			return;
		}
		
		rawXmlMsg = xMessage.createRequestForResultStateResource( tid, iid, secret, role) ;
		
		// this uses ALWAYS Registration as a callback, which is wrong for request about available results !
		 
									out.print(3, "sendRequestForResultStateResource(), sending the request package for transaction tid="+tid);
		xRegistration.sendRequestPackage(rawXmlMsg)  ;
		 
		int ix = mrdProcesses.indexOf(tid);
		if (ix>=0){
			mrdProcesses.remove(ix) ;
		}
		
	}
	
	protected void sendRequestForResultStateResource( Transaction transaction , String role, String additionalIdList){
		
		// additionalIdList = "1;2;3"
		
	}
	
	protected void sendEncapsRequestForResultStateResource( Transaction transaction , String role){
		int mrdProcessCount=0;
		
		mrdProcessCount++;
		// calls sendRequestForResultStateResource() in its own thread, using "MessageDelivery{}"
		try{
			//MessageDelivery mrd =
			(new MessageDelivery()).create(transaction, role, mrdProcesses).go() ;
			
		}catch(Exception e){
			// educated silence
		}
		mrdProcessCount--;
	}
	
	/*
	 * iid = xRegistration.getInstanceID() ;
			// send a message to MessageBoard: available
			sendDigesterStateMsg( XRegistration.RST_IS_AVAIL, -1, "" );
	 */
	
	class MessageDelivery implements Runnable{
		
		Thread mdThrd;
		boolean isRunning=false, isActivated=false;
		
		Transaction transaction ;
		String role;
		
		public MessageDelivery( ){ 
		}
 
		public MessageDelivery create( Transaction transaction , String role, Vector<Long> mrdProcesses ){
			
			long tid = transaction.getTransactionID() ;
			if (mrdProcesses.indexOf(tid)>=0){
							out.print(5, "last check for results on transaction "+tid+" not yet completed, "+
										 "hence not initiating a new check about it.");
				delay(10);
				return null;
			}else{
							out.print(2, "going to check for results on transaction "+tid+" ...");
			}
			mrdProcesses.add(tid);
			
			mdThrd = new Thread(this,"MessageDeliverQueue"); 
			
			this.transaction = transaction ;
			this.role = role ;
			
			return this;
		}
		
		public void go(){
			
			if (mdThrd==null){
				return;
			}
			out.delay(100);
			
			mdThrd.start();
			return  ;
		}
		
		public void run() {
			 
			sendRequestForResultStateResource( transaction , role);
		}
		
	 
		
	}

	// =========================================================
	
	// =========================================================
	/**
	 * 
	 * the callback to this class is installed 
	 *   - in class "MessageReceptor{}" for RECEPTORs, ReceptorDigestIntf taskPerformer;
	 *   - in class "DataConnector{}" for SOURCEs, ReceptorDigestIntf resultsReceiver ; 
	
	
	 */
	public class MessageReceptorDigester implements ReceptorDigestIntf{
		
		MessageBoardFactory mbf ;
		int regTicket;
		boolean sendingConfirmed=false;
		XRegistration srcReg = null;
		MessageBoardParticipant parent ;
		// TaskPackage resultPackage;
		
		public MessageReceptorDigester( MessageBoardFactory mbf , MessageBoardParticipant pparent){
			this.mbf = mbf ;
			parent = pparent ;
			srcReg = xRegistration ;
		}
	
		public void setResultPackage(TaskPackage resultpackage){
			resultPackage = resultpackage;
		}
		
		public void setSourceRegistration( XRegistration xreg){
			srcReg = xreg ;
		}
		
		public XRegistration getSourceRegistration( ){
			if (srcReg==null){
				srcReg = xRegistration ;
			}
			return srcReg  ;
		}
		
		public void setTicketValue( int tv ){
			regTicket = tv;
		}
		
		/**
		 * 
		 * xreg is not-null, if we have loaded an existing (hibernated) registration
		 * 
		 * @param physReceptor
		 * @param xreg
		 */
		public void initializeMessageBoardConnection( int physReceptor , XRegistration xreg){
			 
			// passing the reference to the callback point (ReceptorDigestIntf) , 
			// this creates a RECEPTOR in the factory, and returns the registration object;
			// methods of this object are not needed on this side of the story, it is just for "data convenience"
			
						    RegisterParamPackage rpp = mbf.getRegisterParamPackage();
							
							rpp.regTicket = regTicket; 
							rpp.name = name ; 
							rpp.flavor = flavor;
							rpp.resultsReceiver = mrd ;
							rpp.xreg = xreg ;
							rpp.flavor = flavor;
							rpp.flag = physReceptor; 
							
			xRegistration = mbf.openReceptor( rpp ); //  regTicket, name, xreg, mrd, flavor, physReceptor ) ;  
			
			if (xRegistration!=null) {
				out.printErr(1, "\n"+xRegistration.getRole()+" registered with instance-ID = " + xRegistration.getInstanceID() );
			}
		}
	
		public void sendTaskCompletionStateMsg( long transactionID, double completion){
			String instanceDNA;
			int p;
			long iid;
			Transactions transactions;
			Transaction transaction;
			
			/*
			transactions = mbf.getTransactions() ;
			transaction = transactions.getTransactionByID(transactionID) ;
			if (transaction.getTransactState()==Transaction.TS_TASK_CANCELED ){
				return;
			}
			*/
			if (xRegistration.getTasksBeingCanceled().indexOf(transactionID )>=0){
				return ;
			}
			instanceDNA = this.toString();
	
			iid = this.parent.xRegistration.getInstanceID() ;
			
			p=instanceDNA.indexOf("@");
			if (p>0){
				instanceDNA = instanceDNA.substring(p, instanceDNA.length()) ;
			}
			// "+this.srcReg.getInstanceID()
			sendDigesterStateMsg( XRegistration.RST_IS_IN_PROCESS, transactionID, "task completion : "+completion+"% ::details (by "+instanceDNA+", id:"+iid+")");
		
		}
		
	
		/**
		 * in some use cases, the displayed state flag is transported by taskPackage.getStateFlag() 
		 * 
		 * @param transactionID
		 * @param stateFlag
		 */
		private void handling_deliveryConfirmation( long transactionID, int stateFlag ){
			
			Transactions transactions;
			Transaction transaction;
			
			out.print(2, "Delivery of results (tid:"+transactionID+") has been confirmed ("+stateFlag+")");
			out.print(4, "... arrived in <MessageBoardParticipant> : successful = "+stateFlag) ;
			
			// each delivery is inserted into a list
			deliveryPad.digestingConfirmation(transactionID) ;
			
			if (transactionID>0){
				transactions = mbf.getTransactions() ;
				transaction  = transactions.getTransactionByID(transactionID );
				
				if (transaction!=null){
					transaction.setTransactState( Transaction.TS_RESULTS_RETURNED);
					transaction.setTransactErrorState( stateFlag );
					transactions.save();
				}
			}
			
			if (acceptedTransactionIDs.indexOf(transactionID)>=0){
				acceptedTransactionIDs.removeElement(transactionID ) ;
			}
			setBusyFlag(false);
		}
	
		private void routeTaskToDigestingMethod( TaskPackage taskPackage){
			boolean hb ;
			long tid = -1 ;
			
			mTransact = taskPackage.getTransactionMirror() ;
			
												out.print(5, "task digesting instance : "+ this.toString()); 
			// send a message to MessageBoard: busy
			tid = taskPackage.getTransactionID();
			// iid = xRegistration.getInstanceID() ;
			
			// 
			if (acceptedTransactionIDs.indexOf(tid)>=0){
				// do NOT return a cancel message, just do nothing...
				// we simply got the same task 2 times...
				return;
			}
			
			acceptedTransactionIDs.add(tid) ;
			
			// sendDigesterStateMsg( XRegistration.RST_IS_RUNNING ,tid , "");
			
			 
			sendReceptorsDeliveryConfirmationtoBoard( mTransact );
			
			hb = getBusyState();
			
			setBusyFlag(true);
			
			if (hb){
				
			}
			// working on the task, defined as abstract here (i.e. above in the embedding class)
			// we need o start this from threaded inner class, since there are continuous 
			// ping messages which have to be responded to
			new MessageDigester(taskPackage,2); // 2=for receptor
			
		}
	
		private void assimilateResultsToSource( TaskPackage resultPackage){
			// thread-based separation of incoming results
			out.print(3,"source instance -> MessageBoardParticpant :: MessageDigester :: assimilateResultsToSource()");
			new MessageDigester(resultPackage,1);
		
		}
	
		/**
		 *  SOURCEs and RECEPTORs get the packages here through a callback arranged 
		 *  through type ReceptorDigestIntf , mainly called from MessageReceptor
		 */
		public void performTask( TaskPackage taskPackage ) {
												out.print(3,"message receptor digester ("+parent.name+"): data call back entered, working on task package string...");
			String  str, proceedings="" ;
			
			 
			proceedings = taskPackage.getTypeOfProceedings() ; // -> RECEPTOR = task,story -> SOURCE = results, state
			
			if ( (proceedings.contentEquals("task")) || (proceedings.contentEquals("story"))){
				// proceeding to a "RECEPTOR";
				
				
				str = taskPackage.getDataString() ;
				
				if ((str.contentEquals("cancel")) || (str.contains("cancel::"))){
					
					handlingCancelRequest( taskPackage, 1 );
					setBusyFlag(false);
					
				} else{
					setBusyFlag(true);
					
					out.print(3,"performTask(), calling routeTaskToDigestingMethod()...") ;
					
					routeTaskToDigestingMethod(taskPackage) ;
				}
				
				return;
			}
			
			if ( proceedings.contentEquals("results::list")){
				out.printErr(3,"performTask(), results::list ...") ;
			}
					
			if ( (proceedings.contentEquals("results")) || (proceedings.contentEquals("state"))){
				// proceeding to a "SOURCE";
				assimilateResultsToSource(taskPackage) ; // here the taskPackage is a resultsPackage !
				return;
			}
			
			if ( proceedings.contains("subscription")){
				
			}
			if ( proceedings.contains("failedTransaction")){
				
				handlingFailedTransactions( taskPackage );
				setBusyFlag(false);
				return;
			}
		 
			
			if ( proceedings.contains("stateInformation")){
				
				setBusyFlag(false);
				return;
			}
			
			if ( proceedings.contains("stop::")){
				
				handlingCancelRequest( taskPackage,2 );
				setBusyFlag(false);
			}
			
			if ( proceedings.contentEquals("confirmation")){
				
				handling_deliveryConfirmation( taskPackage.getTransactionID(), taskPackage.getStateFlag() );
				return;
			}
		}
	
		/**
		 * this is ONLY for iBoards, for participants the MessageBoard is null!
		 */
		public void performTaskRelay(String rawXmlStr){
			
			// ParticipantFactory factory;
			
			MessageBoard  mb = factory.getMsgBoardReference() ;
			// MessageBoard mb = factory.getMbf().getMessageBoard() ;
			//((MessageBoardSrcInstance)(factory.parent)). ; MessageBoardSrcInstance
			// Object obj = factory.getInstance(); // .parent -> MessageBoardSrcInstance which is not public.... 
		
			// MessageBoardParticipant  mbpart = mbf.getParticipantReference();
			
			// ParticipantFactory instance = (ParticipantFactory)obj;
			if (mb!=null){
				mb.update( null, rawXmlStr) ;
			}
			// IboardsReplyHandler iboardsReplyHandler = new IboardsReplyHandler( mb, out);
			// iboardsReplyHandler.interpretingMessage(rawXmlStr) ;
		}
		
		
		
		public void performSystemTask(String taskParameters) {
			String xmlstr,str ;
			int vi=-1;
			int p; 
			 
			if (taskParameters.startsWith("restart")){
				// TODO: actually only after a waiting some moments... 
				// needs a handler
				setBusyFlag( true );
				connectingInterrupted = true;
				
				p= taskParameters.indexOf("::");
				if (p>0){
					str = taskParameters.substring(p+2, taskParameters.length()) ;
					long prid=-1;
					p= str.indexOf("=");
					if (p>0){
						str = str.substring(p+1, str.length());
						if ((str.length()>0) &&(strgutil.isNumericX(str))){
							prid = Long.parseLong(str) ;
						}
					}
					
					handlingRegistrationRestart(prid);
					
				}else{
				
					connect( parent.participantsRole, true ) ;
				}
				
				
			}
		
		
			if (taskParameters.contains("shutdown")){
				stop();
				System.exit(0) ;
			}
			
			if (taskParameters.contains("disconnected")){
				
				delayedReConnect( participantsRole, false ) ;
			}
			
			if (taskParameters.contains("startup::complete")){
	
				out.printErr(3, "performSystemTask, completing startup, calling handlingOnStartupCompletion() ...");
			
				if (taskParameters.contains("::complete")){
					handlingOnStartupCompletion();
				}
				if (taskParameters.contains("::getconfirm")){
					handlingOnStartupCompletion();
				}
				return;
			}
			
			if (taskParameters.contains("ping::")){
				// taskParameters = "ping::" + repliedStr
				xmlstr = taskParameters.replace("ping::", "") ;
				sendEchoResponse( xmlstr );
				return;
			}
			
			
			// this state reply is NOT about the task, but about the state of the receptor instance itself
			if (taskParameters.contains("state::")){
				
				// "state::"+Transaction.TS_TASK_ACCEPTED
				xmlstr = taskParameters.replace("state::", "") ;
				if (xmlstr.contains("xml")){
					p= xmlstr.indexOf("::") ;
					if (p>0){
						str = xmlstr.substring( 0,p).trim() ; 
						xmlstr = xmlstr.substring(p+2, xmlstr.length()).trim() ;
						
						if (strgutil.isNumericX(str) ){
							vi = Integer.parseInt(str) ;
							sendStateMessageForMessageBoard(vi,xmlstr,"instance");
						}
					}
				
				}else{
					 
					currentStateMessage(xmlstr) ;
					
				}
				
				return;
			}
			
			if (taskParameters.contains("cancel::")){
				
				if (taskParameters.contains("::all")){
					handlingTransactionCancellation(-1);
				}
				if (taskParameters.contains("::id")){
					// "cancel::id=1023"
					p= taskParameters.indexOf("::") ;
					if (p>0){
						 
						str = taskParameters.substring(p+2, taskParameters.length()).trim() ;
						
						if (strgutil.isNumericX(str) ){
							vi = Integer.parseInt(str) ;
							handlingTransactionCancellation(vi);
						}
					}
					
				}
				
				
				 
			}
			// e.g. "complete restart", "stop deliveries", "hibernate", connection test via ping (echo expected) 
		}
	
		@Override
		public void handlingNonXmlContent(String str) {
			// provide it to client, if it wishes to do so 
			// use reflection here
			
		}
		
	
		@Override
		public void setSendingConfirmed(boolean flag) {
			 
			sendingConfirmed = flag ;
		}


		class MessageDigester implements Runnable{
			Thread digThrd;
			TaskPackage localPackage;
			int target;
			
			public MessageDigester( TaskPackage taskpackage , int target){
				localPackage = taskpackage;
				this.target = target;
				digThrd = new Thread(this,"Receptor-MessageDigester");
				digThrd.start();
			}
			public void run() {
				if (target<=1){
					performTowardsSource();
				}
				if (target>=2){
					performTowardsReceptor();
				}
			}
			private void performTowardsSource(){
				long transactionID;
				
				transactionID = localPackage.getTransactionID() ;
				 
				deliveringReceivedMsg( localPackage ) ;
				 
			}
			
			private void performTowardsReceptor(){
				String ctyp , str="";
				boolean rB;
				long tid;
				int p;
			
												out.print(5,"performTowardsReceptor ...") ;
				
				taskIsCanceled = false;
				
				tid = localPackage.getTransactionID();
				
				p = excludedTransactionIDs.indexOf(tid) ;
				// after 1 block we allow it again..
				if ( excludedTransactionIDs.size()>0){
					excludedTransactionIDs.remove(0);
				}
				if (p>=0){
					// the TransactionID was still on the list of banned id's, 
					// so: just doing nothing
					// return ;
				}
				
				// the method has to set it to false, if something goes wrong, 
				// the task will be automatically canceled ...
				rB = true;
				// 

				setBusyFlag(true);
				
				resultPackage = digestingReceivedMsg( localPackage ) ;
	
				if (resultPackage!=null) {
					ctyp = resultPackage.getContentType();
					str = resultPackage.getTypeOfProceedings() ; // stop::code=-17
					
					if ((ctyp!=null) && (ctyp.contains("RESULT") && (str.startsWith("-")==false))) {
						taskIsCanceled = false;
					}
					if ((ctyp==null) || (resultPackage.getStateFlag()==Transaction.TS_TASK_CANCELED)){
						taskIsCanceled = true;
					}
				}
				
				if (taskIsCanceled){
					// no sending
					sendDigesterStateMsg( XRegistration.RST_IS_AVAIL, -1,"participants busyState" );
					setBusyFlag(false);
					return;
				}
				// if (taskIsCanceled==false)
				{
					resultPackage.setTransactionID( tid ) ;
															
				}
				xRegistration.getRole() ;	
				
				// put the results to a dedicated sender class, which 
				// cares for delivery, confirmation... !!
				
				
															out.print(3,"     ... sending new state value (RST_IS_AVAIL) to board...") ;
				// define the state of the connection of this object, such that it can be queried
				xRegistration.setConnectionState( DataConnector.CNX_AVAIL );
	
				
															out.print(3,"     ... returning results.") ;
															
				// send a message to MessageBoard: return message (result package, derived from taskpackage)
				sendingConfirmed=false;
                sendResultPackage( );
                
				excludedTransactionIDs.add( localPackage.getTransactionID()) ;
				
															out.print(3,"     ... results returned, again checking for new tasks ...\n") ;
				// NOT YET !!!! only upon confirmation, or failure of confirmation..., or after 5 additional seconds
				// setBusyFlag(false);
										
				int z=0;
				while ((sendingConfirmed == false) && (z<200)){
					delay(10); z++;
					// not completely correct, since large messages could take a long time
					// if then sending a large message takes longer than getting the next msg and digesting it
					// we will have a problem ? ... not really, since we use the deliveryPad as a sending device
				}
				// after 2 seconds, release it anyway
				setBusyFlag(false);
				
				// accordingly, send a message to MessageBoard: available
				sendDigesterStateMsg( XRegistration.RST_IS_AVAIL, -1,"participants busyState" );
			}
			
		}
	
		
	} // inner class

	/**
	 * where is the period defined ?
	 * MessageBoardFactoryProperties::PeriodLengthBetweenPollsForTasks
	 * .
	 * 
	 */
	class UpLinkConnector implements ParticipantsUpLinkConnectorIntf{
		
		int remainingPauseCycles=0, performedCycles=0;
		boolean busyFlag=false;
		int z=0;
	 
		int itechwait=0; 
		Map<Long,Integer> transactionByPassed = new HashMap<Long,Integer>();
		
		
		
		public UpLinkConnector(){
			
		}
	
		 
		
		@Override
		public void checkAvailability() {
			 
			
		}
	
		private boolean refreshRegistrationAtMessageBoard(){
			boolean rB=true;
			long now,thresholdDelta, actualDelta, regTime ;
			
			
		    // send a refresh if it has been defined so in the properties of the participant
			if (mbf.getFactoryProperties().getParticipantFactory().config().isKeepRegistrationAlive()==false){
				return rB;
			}
			
			
			now = System.currentTimeMillis();
			// the time out is sth like 15 min
			thresholdDelta = msgBoardFactoryProperties.getRegistrationTimeOut() - 40000 ;
			
			
			regTime= xRegistration.getRegisteredSince() ;
			actualDelta = now - regTime ;
			if (thresholdDelta - actualDelta < 0){
				
				// send a registrationRefresh to the MessageBoard, if desired
				
				xRegistration.sendKeepAlive() ;
												out.print(3, "keep-alive message sent to message board by "+xRegistration.getRole()+
															 " id="+ xRegistration.getInstanceID()+"..."); 
				xRegistration.setRegisteredSince(now) ;
			}
			
			return rB;
		}
		 
		@Override
		public void checkForTasks(int indicator) {
			// this is called as a callback from the UplinkCheckEngine as part of the 
			// abstract class: ParticipantUpLinkServiceAbs, parenting: ReceptorsUpLinkService ;
			// this engine runs a periodic thread jumping to here
			// in ParticipantFactory we find the typed variable: ReceptorsUpLinkService receptorsUpLinkService;
			// which is returned to here as "upLinkService" through the interface: ParticipantUpLinkServiceIntf
			
			int r,dd=70, periodLen;   
			
			periodLen = msgBoardFactoryProperties.getPeriodLengthBetweenPollsForTasks();
			// periodLen = 35000;  // DEBUG TIME PERIOD
			upLinkService.setPeriodLength( periodLen ) ; // default is 7000, or 12000
			
			// somewhere else 3000 millis are lost: waiting for confirmation by the board ?
			
			// if there are a lot of receptors, the MessageBoard has to command them to use a longer period
			// there should not be more than 100/sec
			// the rule should consider total number of RECEPTORs for a particular type, average time needed to return data
			// "travel" mode receptors should have slight priority
			
			/*
			      this is completely different from the sources-part !
			      we have just 1 request per loop
			      
			      the thread is running in the abstract class
			 */
			boolean hb = getBusyState() ;
			// check whether we are already working...
			if ((busyFlag) || (xRegistration==null)){
				return;
			}
			
			if (hb==false){
				int x;
				x=0;
			}
			
			if (remainingPauseCycles>0){
				remainingPauseCycles--;
				if (remainingPauseCycles>0){
					return;
				}
			}
			// if no, so we check if their is some task
			 												
			if (mbf.getFactoryProperties().getProtocolId() < MessageBoardFactoryProperties.protocolTCP){
				// this has to be the MessageReceptor !! 
				// OR: we route this towards it
				mbf.getComBoxFactory().setObserver( mbf.getReceptorObserver() )  ; // this	
			}
			// this should exclude the last transaction id !
			
			if (z>=1){
				delay(dd); // this is a pseudo-non-blocking delay
			}
							out.print(4, "checking for TASKS deposited to the MessageBoard...");
							
			z++;
			if (xRegistration!=null){
				//
				r = sendRequestForTaskPackageResource( xRegistration.getInstanceID(), xRegistration.getRole() ) ;
			
			}
			r=0;
		}
		 
		/**
		 * 
		 * the service will be started in "connect()", yet only if it is set to be allowed 
		 * in ParticipantFactory::getInstance(), the variable being "useUplinkConnectPolling"
		 * 
		 */
		@Override
		public void checkForResults(int indicator) { 
			// will be regularly called, each 25 seconds or so
			int t, x,xsv, stateVal, errStateVal,n,ix;
			boolean currentlySending = false;
			int periodLen;
			String str, role="" ;
			long[] tids ;
			Vector<Long> waitingIDs = new Vector<Long>();
			
			Transactions transactions;
			Transaction transaction;
			
			int[] openStates = new int[]{ Transaction.TS_TASK_CONFIRMED, Transaction.TS_OPENED,
										  Transaction.TS_TASK_DIGESTING, Transaction.TS_TASK_ACCEPTED, 
										  Transaction.TS_RESULTS_BUFFERED};
			Arrays.sort(openStates) ;
			
			// periodLen = 35000;  // DEBUG TIME PERIOD
			// upLinkService.setPeriodLength( periodLen ) ;
			
			performedCycles++;
			
			if (performedCycles % 50 == 0){
												out.print(4, "refreshing registration at MessageBoard ...");
				refreshRegistrationAtMessageBoard() ;
				
				if (performedCycles>1000000){
					performedCycles=1;
				}
			}
			
			if (remainingPauseCycles>0){
				remainingPauseCycles--;
				if (remainingPauseCycles>0){
					return;
				}
			}
			
			xsv = xRegistration.getReceptorState();
			if ((xRegistration==null) || 
				(xRegistration.getCurrentlySending()) || 
				(xsv < XRegistration.RST_IS_AVAIL)){
				
				return;
			}
			
			// refreshing this value, it could have been changed by some intervention of the MessageBoard
			periodLen = msgBoardFactoryProperties.getPeriodLengthBetweenPollsForTasks();
			upLinkService.setPeriodLength( periodLen );
			
												out.print(4, "checkForResults()...");

			
	        
			transactions =  mbf.getTransactions();
			tids = transactions.getOpenTransactions( openStates ) ;
			t=tids.length ;
			
			if (t<3){
				// shorten the interval period length to 2 seconds
				
			}else{
				
			}
			
												if (t>0){
													out.print(4, "checking for results (waiting:"+t+") deposited to the MessageBoard...");
												}
												
												if (performedCycles % 10 == 0){
													out.print(3, "checking for results (waiting:"+t+") deposited to the MessageBoard...");
												}
if (t>0){
	x=0;
}
							
            x=0;
            // TODO: if we are in TCP mode, then ... is it really different ?
            //       the MessageBoard should block / disregard parallel requests
			Vector<Transaction> transacts = transactions.getTransactions() ;
			// doing it in this way, we always ask first for the oldest transaction, even after re-start and delivery of new tasks
			// TODO: we should also provide the possibility to transmit the whole list at once !!! 
			int i=0; 
			while (i<t){
					
				if ((i>=transacts.size()) || (xRegistration==null) || (transactions==null) || (remainingPauseCycles>0)){
					break;
				}
				
				if (i>=tids.length){
					break;
				}
				
				
				
				ix = transactions.getIndexByTransactionID( tids[i],1) ;
				transaction = transacts.get(ix);
				
				stateVal = transaction.getTransactState() ;
				str = Transaction.decodeTransactionCode(stateVal) ;
				
				errStateVal = transaction.getTransactErrorState() ;
				if (errStateVal != Transaction.TS_ERR_NO){
					i++;
					continue;
				}
// TODO: upLinkPollingForAnyItem -> requires a different xml request
				
				int vp = Arrays.binarySearch(openStates, stateVal) ;
				// if ( (stateVal == Transaction.TS_TASK_CONFIRMED ) || (stateVal == Transaction.TS_OPENED )
				if (vp>=0){
				// it might be opened, while confirmation failed... it nevertheless would sit over there
				// do this optional, controlled by a variable
																
												out.print(5, "checking transaction (id="+transaction.getTransactionID()+") for results...");
					// ?? open transactions dictionary will be reloaded ...
				
				role = xRegistration.getRole() ;
					 
					// transaction.setCurrentlyBeingChecked(true); avoiding re-check of the same while the check is running 
					// there, also set : setLastTimeForResultCheck()... for a timeout of the pause
					if ( msgBoardFactoryProperties.isTransmittingListsAllowed()){
						// we prepare a list, which contains all the IDs except the first one
						if ((i==0) && (t-2>0)){
							
							
							for (int j=1;j<t;j++){
								waitingIDs.add( tids[j] );
							}
							str = strgutil.arr2text(waitingIDs, ";");
							sendRequestForResultStateResource( transaction, role , str) ;
						}
						break;
					}else{
						
						long tid = transaction.getTransactionID();
						
						if (currentlySending==false){
							// this request takes some time, so, overall it is slow -> a class with max n concurrent processes doing this 
							// sendRequestForResultStateResource( transaction, role ) ;
							
							 
							// if (remainingPauseCycles<=0)
							{
								// will decrease count down value by 1
								if (transactionToRequest(tids[i]) ){
									
									currentlySending = true;
									
									out.print(4, "checkForResults(), tid="+tid+" sending the request...");
									
									pause( tids[i],5);
									sendRequestForResultStateResource(transaction, role) ; 
									// in order to avoid overlapping calls, we have to exit here
									// but then we would test always the first that fails 
									currentlySending=false;
									break;
								} else{ 
									n=-1;
									if (transactionByPassed.containsKey(tid)){
										n = transactionByPassed.get( tid );
									}
									out.print(5, "checkForResults() skipping check for transaction tid="+tid+
											     ", its remainingPauseCycles="+n);
									
								}
								
								// will reset the count down value: transactionByPassed.put(transactionID, cycles); 
								//  // test this transaction only each 5th encounter
								// does not make much sense with the map "transactionByPassed" being a local 
							} 
							// like "sendRequest...", but using its own thread, yet, sendencaps does not work...
							/*
							delay(80);
							if (mrdProcesses.size()>20){
								delay(50);
							}
							// delay(80000);
							 */
							 
							// currentlySending=false;
						}
						/* reworking _MessageDelivery, which currently is not in use 
						 * else, it seems that under TCP transactions are not marked as closed on the server
						 * 
						 * and similar, if tcp, previous results are not recognized by the MessageBoard -> TEST !!
						 * 
						 */
						
						
					}
					delay(5) ;
					
				}else{
				 
				}
				t = transacts.size();
				i++;
			} // i->
			
		}
	
		private boolean transactionToRequest( long transactionID){
			boolean rB=true;
			
												out.print(4, "transactionToBypass(), checking transaction ID for uplink check");
			if (transactionByPassed.containsKey( transactionID)){
												out.print(4, "transactionToBypass(), transaction (tid:"+transactionID+") found...");
				// a transaction is excluded from uplink checking if it currently is receiving
				// a message about results;
				int remainingPauseForTid = transactionByPassed.get( transactionID);
				if (remainingPauseForTid >0){
					remainingPauseForTid = remainingPauseForTid-1;
					transactionByPassed.put(transactionID, remainingPauseForTid) ;
					rB=false;
				}else{
					transactionByPassed.remove(transactionID);
				}
			}
			return rB;
		}
		
		
		@Override
		public int getExitValue() {
			 
			return 0;
		}
	
	
	
		@Override
		public void setBusyFlag(boolean value) {
			 busyFlag = value;
			
		}
	
	
	
		@Override
		public boolean getBusyFlag() {
		 
			return busyFlag;
		}


		/**
		 * bypass checks for all transactions
		 */
		@Override
		public void pause(int cycles) {
			 
			remainingPauseCycles = cycles;
		}


		/**
		 * bypass check just for provided tid
		 */
		@Override
		public void pause(long transactionID, int cycles) {
			itechwait=1;
			if (transactionByPassed.containsKey(transactionID)){
				transactionByPassed.remove(transactionID) ;
			}
												out.print(4, "adding transaction id <"+transactionID+"> to list of exceluded uplink checks...");
			transactionByPassed.put(transactionID, cycles);
			itechwait=0;
		}
	
	}
	
	// inner class
	// =========================================================
}


















